/**
* Copyright (C) 2009 BonitaSoft S.A.
* BonitaSoft, 31 rue Gustave Eiffel - 38000 Grenoble
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2.0 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.bonitasoft.forms.client.view.widget;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.bonitasoft.forms.client.i18n.FormsResourceBundle;
import org.bonitasoft.forms.client.model.FileWidgetInputType;
import org.bonitasoft.forms.client.model.ReducedFormFieldAvailableValue;
import org.bonitasoft.forms.client.view.SupportedFieldTypes;
import org.bonitasoft.forms.client.view.common.DOMUtils;
import org.bonitasoft.forms.client.view.common.RpcFormsServices;
import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.FormElement;
import com.google.gwt.event.dom.client.ChangeEvent;
import com.google.gwt.event.dom.client.ChangeHandler;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.logical.shared.ValueChangeEvent;
import com.google.gwt.event.logical.shared.ValueChangeHandler;
import com.google.gwt.http.client.URL;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.FileUpload;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.FormPanel;
import com.google.gwt.user.client.ui.FormPanel.SubmitCompleteEvent;
import com.google.gwt.user.client.ui.FormPanel.SubmitCompleteHandler;
import com.google.gwt.user.client.ui.FormPanel.SubmitEvent;
import com.google.gwt.user.client.ui.FormPanel.SubmitHandler;
import com.google.gwt.user.client.ui.Image;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.TextBox;
/**
* Widget displaying either a file upload input if its initial value is null or a download link and file upload input to overide the current file
*
* @author Anthony Birembaut
*/
public class FileUploadWidget extends Composite implements ValueChangeHandler<Boolean> {
/**
* The URL document type
*/
public static final String URL_DOCUMENT_TYPE = "URL";
/**
* The file document type
*/
public static final String FILE_DOCUMENT_TYPE = "file";
/**
* the flow panel used to display the widgets
*/
protected FlowPanel flowPanel;
/**
* the form panel used to display the widget
*/
protected FormPanel formPanel;
protected FlowPanel buttonPanel;
protected Label cancelLabel;
protected Label modifyLabel;
protected Label removeLabel;
protected FileDownloadWidget fileDownloadWidget;
protected FileUpload fileUpload;
protected Image loadingImage;
protected String uploadedFilePath;
protected String fileUploadFormName;
protected long attachmentId;
protected String attachmentName;
protected FlowPanel filePanel;
protected TextBox urlTextBox;
protected RadioButtonGroupWidget radioButtonGroupWidget;
protected FileWidgetInputType fileWidgetInputType;
protected boolean isInitialContentFile;
/**
* Constructor
*
* @param contextMap
* @param fieldId
* @param fileWidgetInputType
* @param valueType
* @param attachmentId
* @param attachmentName
* @param value
* @param hasImagePreview
* @param isElementOfMultipleWidget
*/
public FileUploadWidget(final String formID, final Map<String, Object> contextMap, final String fieldId, final FileWidgetInputType fileWidgetInputType,
final String valueType, final long attachmentId, final String attachmentName, final String value, final boolean hasImagePreview) {
this.fileWidgetInputType = fileWidgetInputType;
this.attachmentId = attachmentId;
this.attachmentName = attachmentName;
if (attachmentName != null) {
fileUploadFormName = attachmentName;
} else {
fileUploadFormName = fieldId;
}
isInitialContentFile = SupportedFieldTypes.JAVA_FILE_CLASSNAME.equals(valueType);
flowPanel = new FlowPanel();
if (isValidInputType(fileWidgetInputType, value)) {
buildView(formID, contextMap, fieldId, valueType, value, hasImagePreview);
}
initWidget(flowPanel);
}
protected void buildView(final String formID, final Map<String, Object> contextMap, final String fieldId, final String valueType, final String value,
final boolean hasImagePreview) {
if (FileWidgetInputType.ALL.equals(fileWidgetInputType)) {
final List<ReducedFormFieldAvailableValue> availableValues = new ArrayList<ReducedFormFieldAvailableValue>();
availableValues.add(new ReducedFormFieldAvailableValue(FormsResourceBundle.getMessages().url(), URL_DOCUMENT_TYPE));
availableValues.add(new ReducedFormFieldAvailableValue(FormsResourceBundle.getMessages().file(), FILE_DOCUMENT_TYPE));
final String initialRadioButton;
if (isInitialContentFile) {
initialRadioButton = FILE_DOCUMENT_TYPE;
} else {
initialRadioButton = URL_DOCUMENT_TYPE;
}
radioButtonGroupWidget = new RadioButtonGroupWidget(fieldId + "_document_type", availableValues, initialRadioButton, "bonita_form_radio_inline",
false, true);
radioButtonGroupWidget.addValueChangeHandler(this);
flowPanel.add(radioButtonGroupWidget);
final FlowPanel clearFloatPanel = new FlowPanel();
clearFloatPanel.setStyleName("bonita_clear_float");
flowPanel.add(clearFloatPanel);
}
if (!FileWidgetInputType.URL.equals(fileWidgetInputType)) {
createFileUploadForm(fileUploadFormName);
filePanel = new FlowPanel();
fileDownloadWidget = new FileDownloadWidget(formID, contextMap, valueType, attachmentId, hasImagePreview);
loadingImage = new Image("themeResource?theme=portal&location=images/ajax-loader.gif");
loadingImage.setTitle(FormsResourceBundle.getMessages().uploadingLabel());
buttonPanel = new FlowPanel();
cancelLabel = new Label();
cancelLabel.setText(FormsResourceBundle.getMessages().cancelButtonLabel());
cancelLabel.setTitle(FormsResourceBundle.getMessages().cancelButtonTitle());
cancelLabel.setStyleName("bonita_upload_button");
modifyLabel = new Label();
modifyLabel.setText(FormsResourceBundle.getMessages().modifyButtonLabel());
modifyLabel.setTitle(FormsResourceBundle.getMessages().modifyButtonTitle());
modifyLabel.setStyleName("bonita_upload_button");
removeLabel = new Label();
removeLabel.setText(FormsResourceBundle.getMessages().removeButtonLabel());
removeLabel.setTitle(FormsResourceBundle.getMessages().removeButtonTitle());
removeLabel.setStyleName("bonita_upload_button");
buttonPanel.add(cancelLabel);
buttonPanel.add(modifyLabel);
buttonPanel.add(removeLabel);
buttonPanel.addStyleName("bonita_upload_button_group");
loadingImage.setVisible(false);
if (value != null && isInitialContentFile) {
fileDownloadWidget.setFileName(value);
formPanel.setVisible(false);
} else {
fileDownloadWidget.setVisible(false);
modifyLabel.setVisible(false);
removeLabel.setVisible(false);
}
cancelLabel.setVisible(false);
filePanel.add(formPanel);
filePanel.add(loadingImage);
filePanel.add(fileDownloadWidget);
filePanel.add(buttonPanel);
modifyLabel.addClickHandler(new ClickHandler() {
@Override
public void onClick(final ClickEvent event) {
formPanel.setVisible(true);
fileDownloadWidget.setVisible(false);
modifyLabel.setVisible(false);
removeLabel.setVisible(false);
cancelLabel.setVisible(true);
}
});
removeLabel.addClickHandler(new ClickHandler() {
@Override
public void onClick(final ClickEvent event) {
formPanel.clear();
uploadedFilePath = null;
fileUpload = addFileUploalToFormPanel(fileUploadFormName);
formPanel.setVisible(true);
fileDownloadWidget.setVisible(false);
fileDownloadWidget.resetDownloadlink();
modifyLabel.setVisible(false);
removeLabel.setVisible(false);
cancelLabel.setVisible(false);
}
});
cancelLabel.addClickHandler(new ClickHandler() {
@Override
public void onClick(final ClickEvent event) {
formPanel.setVisible(false);
fileDownloadWidget.setVisible(true);
modifyLabel.setVisible(true);
removeLabel.setVisible(true);
cancelLabel.setVisible(false);
}
});
flowPanel.add(filePanel);
}
if (!FileWidgetInputType.FILE.equals(fileWidgetInputType)) {
urlTextBox = new TextBox();
if (value != null && !isInitialContentFile) {
urlTextBox.setValue(value);
}
flowPanel.add(urlTextBox);
}
if (radioButtonGroupWidget != null) {
if (FILE_DOCUMENT_TYPE.equals(radioButtonGroupWidget.getValue())) {
urlTextBox.setVisible(false);
filePanel.setVisible(true);
} else {
filePanel.setVisible(false);
urlTextBox.setVisible(true);
}
}
}
protected boolean isValidInputType(final FileWidgetInputType fileWidgetInputType, final String value) {
if (value != null) {
if (FileWidgetInputType.URL.equals(fileWidgetInputType) && isInitialContentFile) {
displayErrorMessage(FormsResourceBundle.getErrors().wrongContentOfTypeFileError());
return false;
} else if (FileWidgetInputType.FILE.equals(fileWidgetInputType) && !isInitialContentFile) {
displayErrorMessage(FormsResourceBundle.getErrors().wrongContentOfTypeURLError());
return false;
}
}
return true;
}
protected void displayErrorMessage(final String message) {
final Label errorMessage = new Label(message);
errorMessage.addStyleName("callout-danger callout");
flowPanel.add(errorMessage);
}
protected void createFileUploadForm(final String FileUloadName) {
formPanel = new FormPanel();
formPanel.setEncoding(FormPanel.ENCODING_MULTIPART);
formPanel.setMethod(FormPanel.METHOD_POST);
FormElement.as(formPanel.getElement()).setAcceptCharset("UTF-8");
formPanel.setAction(RpcFormsServices.getFileUploadURL());
fileUpload = addFileUploalToFormPanel(FileUloadName);
}
protected FileUpload addFileUploalToFormPanel(final String fileUploadName) {
final FileUpload fileUpload = new FileUpload();
fileUpload.addChangeHandler(new ChangeHandler() {
@Override
public void onChange(final ChangeEvent event) {
formPanel.submit();
}
});
fileUpload.setStyleName("bonita_file_upload");
// mandatory because we are in a true form with a post action
fileUpload.setName(fileUploadName);
if (DOMUtils.getInstance().isIE8()) {
fileUpload.getElement().setPropertyString("contentEditable", "false");
}
formPanel.add(fileUpload);
final UploadSubmitHandler uploadHandler = new UploadSubmitHandler();
formPanel.addSubmitHandler(uploadHandler);
formPanel.addSubmitCompleteHandler(uploadHandler);
return fileUpload;
}
protected class UploadSubmitHandler implements SubmitHandler, SubmitCompleteHandler {
protected String filePath;
/**
* {@inheritDoc}
*/
@Override
public void onSubmit(final SubmitEvent event) {
filePath = fileUpload.getFilename();
if (filePath == null || filePath.length() == 0) {
event.cancel();
} else {
formPanel.setVisible(false);
loadingImage.setVisible(true);
}
}
/**
* {@inheritDoc}
*/
@Override
public void onSubmitComplete(final SubmitCompleteEvent event) {
String response = getPlainTextResult(event);
response = URL.decodeQueryString(response);
response = response.replaceAll("&", "&");
response = response.replaceAll("<", "<");
response = response.replaceAll(">", ">");
uploadedFilePath = response;
String realFileName = null;
if (filePath.contains("\\")) {
realFileName = filePath.substring(filePath.lastIndexOf("\\") + 1);
} else if (filePath.contains("/")) {
realFileName = filePath.substring(filePath.lastIndexOf("/") + 1);
} else {
realFileName = filePath;
}
final int divIndex = realFileName.indexOf("<div");
if (divIndex > 0) {
realFileName = realFileName.substring(0, divIndex);
}
loadingImage.setVisible(false);
fileDownloadWidget.setFilePath(uploadedFilePath, realFileName);
fileDownloadWidget.setVisible(true);
modifyLabel.setVisible(true);
removeLabel.setVisible(true);
cancelLabel.setVisible(false);
}
/**
* See BS-9072 - To avoid xss attack we return text/plain output in fileuploadservlet.
* But GWT is converting plain text in html element (pre).
* Need to do this hack to get real servlet response
*/
private String getPlainTextResult(final SubmitCompleteEvent event) {
final Element label = DOM.createLabel();
label.setInnerHTML(event.getResults());
return label.getInnerText();
}
}
/**
* @return the path to the uploaded file
*/
public String getValue() {
if (FileWidgetInputType.ALL.equals(fileWidgetInputType)) {
if (FILE_DOCUMENT_TYPE.equals(radioButtonGroupWidget.getValue())) {
return uploadedFilePath;
} else {
return urlTextBox.getValue();
}
} else if (FileWidgetInputType.FILE.equals(fileWidgetInputType)) {
return uploadedFilePath;
} else {
return urlTextBox.getValue();
}
}
/**
* @return the type of the field value
*/
public String getValueType() {
if (FileWidgetInputType.ALL.equals(fileWidgetInputType)) {
if (FILE_DOCUMENT_TYPE.equals(radioButtonGroupWidget.getValue())) {
return SupportedFieldTypes.JAVA_FILE_CLASSNAME;
} else {
return SupportedFieldTypes.JAVA_STRING_CLASSNAME;
}
} else if (FileWidgetInputType.FILE.equals(fileWidgetInputType)) {
return SupportedFieldTypes.JAVA_FILE_CLASSNAME;
} else {
return SupportedFieldTypes.JAVA_STRING_CLASSNAME;
}
}
/**
* Disable the fileupload
*/
public void disable() {
filePanel.remove(formPanel);
filePanel.remove(buttonPanel);
fileDownloadWidget.setVisible(true);
}
@Override
public void onValueChange(final ValueChangeEvent<Boolean> event) {
if (FILE_DOCUMENT_TYPE.equals(radioButtonGroupWidget.getValue())) {
urlTextBox.setVisible(false);
filePanel.setVisible(true);
} else {
filePanel.setVisible(false);
urlTextBox.setVisible(true);
}
}
public String getAttachmentName() {
return attachmentName;
}
public long getAttachmentId() {
return attachmentId;
}
public String getDisplayedValue() {
String displayedValue = null;
if (FileWidgetInputType.ALL.equals(fileWidgetInputType)) {
if (FILE_DOCUMENT_TYPE.equals(radioButtonGroupWidget.getValue())) {
displayedValue = fileDownloadWidget.getDisplayedValue();
} else {
displayedValue = urlTextBox.getValue();
}
} else if (FileWidgetInputType.FILE.equals(fileWidgetInputType)) {
displayedValue = fileDownloadWidget.getDisplayedValue();
} else {
displayedValue = urlTextBox.getValue();
}
return displayedValue;
}
}